package cn.xiongxf.service.impl;

import cn.xiongxf.constant.RedisKeyConstant;
import cn.xiongxf.domain.Blacklist;
import cn.xiongxf.dto.RedisValueDto;
import cn.xiongxf.dto.SmsDto;
import cn.xiongxf.exception.GlobalCustomException;
import cn.xiongxf.properties.SmsVerifyCodeProperties;
import cn.xiongxf.service.IBlacklistService;
import cn.xiongxf.service.IMessageSmsService;
import cn.xiongxf.service.IVerifyCodeService;
import cn.xiongxf.util.StrUtils;
import cn.xiongxf.util.VerifyCodeUtils;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private IMessageSmsService messageSmsService;
    @Autowired
    private SmsVerifyCodeProperties smsProp;
    @Autowired
    private IBlacklistService blacklistService;

    /**
     * 获取图片验证码
     */
    @Override
    public String imageVerifyCode(String key) {
        // 1.获取随机验证码
        String code = StrUtils.getComplexRandomString(4);
        // 2.保存到redis中，设置5分钟有效期
        redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);
        // 3.获取图形验证码编译的字符串
        return VerifyCodeUtils.verifyCode(115, 40, code);
    }

    /**
     * 获取短信验证码
     */
    @Override
    public void sendSmsCode(SmsDto smsDto) {
        // 1.参数校验(JSR303已做)
        // 2.条件校验
        // 2.1.图片验证码正确性校验(忽略大小写)
        String imgcodeDB = (String) redisTemplate.opsForValue().get(smsDto.getImageCodeKey());
        if (! smsDto.getImageCode().equalsIgnoreCase(imgcodeDB)){
            throw new GlobalCustomException("图片验证码不正确");
        }
//        AssertUtil.isEquals(smsDto.getImageCode(),imgcodeDB,"图片验证码不正确");  // 断言工具类,需改造throw的RuntimeException

        // 3.获取短信验证码
        String key = RedisKeyConstant.SMSCODE_KEY_PREFIX + smsDto.getMobile();
        RedisValueDto redisValueDtoDB = (RedisValueDto) redisTemplate.opsForValue().get(key);
        String smsCode;
        if (redisValueDtoDB == null){
            // 3.1.未发送过短信验证码或过期,生成短信验证码
            smsCode = StrUtils.getRandomString(smsProp.getSmsCodeLength());
        }else {
            // 3.2.短信验证码未过期,判断重发时间
            Long lastTime = redisValueDtoDB.getTime();
            long nowTime = System.currentTimeMillis();  // System.currentTimeMillis()相较于new Date().getTime()能更快速地获取时间信息
            if (nowTime - lastTime < smsProp.getResendTime() * 1000){
                // 3.2.1.没过重发时间,给出提示
                throw new GlobalCustomException("请勿频繁获取短信验证码");
            }else {
                // 3.2.2.已过重发时间,重新发送上次的短信验证码
                smsCode = redisValueDtoDB.getSmsCode();
            }
        }

        // # 黑名单校验
        verifyBlacklist(smsDto.getMobile());

        // 4.调用中国网建接口发送短信（如果不在黑名单中）
        String content = String.format(smsProp.getContent(),smsCode,smsProp.getExpireTime());
        log.info(content);

        // 5.保存短信信息到DB
        messageSmsService.save(smsProp.getTitle(),content,smsDto.getMobile());

        // 6.保存短信验证码信息到redis
        RedisValueDto redisValueDto = new RedisValueDto(smsCode,System.currentTimeMillis());
        redisTemplate.opsForValue().set(key, redisValueDto,smsProp.getExpireTime(), TimeUnit.MINUTES);
    }

    private void verifyBlacklist(String phone) {
        // 获取IP地址
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String ip = requestAttributes.getRequest().getRemoteAddr();

        List<Blacklist> blacklists = (List<Blacklist>)redisTemplate.opsForValue().get(RedisKeyConstant.BLACKLIST_KEY);
        if (blacklists == null){
            // 为空，从MySQL中获取黑名单,并缓存到Redis
            blacklists = blacklistService.selectList(null);
            redisTemplate.opsForValue().set(RedisKeyConstant.BLACKLIST_KEY, blacklists);
        }
        // 校验黑名单
        blacklists.forEach(blacklist -> {
            if (phone.equals(blacklist.getPhone()) || ip.equals(blacklist.getIp()))
                throw new GlobalCustomException("您已被禁止发送短信验证码，请联系管理员！");
        });
    }

}
